package cn.wolfcode.p2p.bussness.service.impl;

import cn.wolfcode.p2p.base.domain.Account;
import cn.wolfcode.p2p.base.domain.UserInfo;
import cn.wolfcode.p2p.base.excetion.DisplayableExcetion;
import cn.wolfcode.p2p.base.query.QueryObjext;
import cn.wolfcode.p2p.base.service.IAccountService;
import cn.wolfcode.p2p.base.service.IUserInfoService;
import cn.wolfcode.p2p.base.util.BitStateUtil;
import cn.wolfcode.p2p.base.util.Constants;
import cn.wolfcode.p2p.base.util.UserContext;
import cn.wolfcode.p2p.bussness.domain.*;
import cn.wolfcode.p2p.bussness.mapper.BidMapper;
import cn.wolfcode.p2p.bussness.mapper.BidRequestMapper;
import cn.wolfcode.p2p.bussness.query.BidRequestQueryObject;
import cn.wolfcode.p2p.bussness.service.*;
import cn.wolfcode.p2p.bussness.util.CalculatetUtil;
import com.github.pagehelper.Constant;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.sun.xml.internal.bind.v2.TODO;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Service
@Transactional
public class BidRequestServiceImpl implements IBidRequestService {
    @Autowired
    private BidRequestMapper bidRequestMapper;
    @Autowired
    private IUserInfoService userInfoService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IBidService bidService;
    @Autowired
    private IAccountFlowService accountFlowService;
    @Autowired
    private IBidRequestAuditHistoryService bidRequestAuditHistoryService;
    @Autowired
    private IPaymentScheduleService paymentScheduleService;
    @Autowired
    private IPaymentScheduleDetailService paymentScheduleDetailService;

    @Override
    public int save(BidRequest record) {
        return bidRequestMapper.insert(record);
    }

    @Override
    public BidRequest get(Long id) {
        return bidRequestMapper.selectByPrimaryKey(id);
    }

    @Override
    public int update(BidRequest record) {
        int count = bidRequestMapper.updateByPrimaryKey(record);
        if (count == 0) {
            throw new DisplayableExcetion("乐观锁异常,recordId=" + record.getId());
        }
        return count;
    }

    @Override
    public PageInfo queryPage(BidRequestQueryObject qo) {
        PageHelper.startPage(qo.getPageNum(), qo.getPageSize());
        List list = bidRequestMapper.selectAll(qo);
        return new PageInfo(list);
    }

    //借款提交申请
    @Override
    public void apply(BidRequest bidRequest) {
        UserInfo userInfo = userInfoService.getCurrent();
        Account account = accountService.getCurrent();
        if (userInfo.isCanBorrow() &&    //是否有借款条件
                !userInfo.isHasBidRequestApplyProcess() &&   //没有借款的流程在申请
                bidRequest.getBidRequestAmount().compareTo(Constants.BORROW_AMOUNT_MIN) >= 0 &&//该次借款金额 >= 系统最小借款金额
                bidRequest.getBidRequestAmount().compareTo(account.getRemainBorrowLimit()) <= 0 &&//该次借款金额 <=该账户剩余的授信额度
                bidRequest.getMinBidAmount().compareTo(Constants.BID_AMOUNT_MIN) >= 0 &&//该次的设置最小投标金额>=系统中设置的最小投标金额
                bidRequest.getCurrentRate().compareTo(Constants.CURRENT_RATE_MIN) >= 0 &&//该该次的还款利息>=系统中的最小利息
                bidRequest.getCurrentRate().compareTo(Constants.CURRENT_RATE_MAX) <= 0 //该次的还款利息<=系统中的最大利息
                ) {
            //创建借款对象,设置相关的属性
            BidRequest bb = new BidRequest();
            bb.setApplyTime(new Date());//借款申请时间
            bb.setBidRequestAmount(bidRequest.getBidRequestAmount());//借款金额
            bb.setBidRequestState(Constants.BIDREQUEST_STATE_APPLY);//申请中状态
            bb.setBidRequestType(Constants.BIDREQUEST_TYPE_NORMAL);//借款类型,普通信用标
            bb.setCreateUser(UserContext.getCount());//借款人
            bb.setCurrentRate(bidRequest.getCurrentRate());//借款的利率
            bb.setDescription(bidRequest.getDescription());//借款描述
            bb.setDisableDays(bidRequest.getDisableDays());//招标天数
            bb.setMinBidAmount(bidRequest.getMinBidAmount());//设置最小投标金额
            bb.setMonthes2Return(bidRequest.getMonthes2Return());//借款周期
            bb.setReturnType(Constants.RETURN_TYPE_MONTH_INTEREST_PRINCIPAL);//还款方式,按月分期
            bb.setTitle(bidRequest.getTitle());//借款标题
            //借款的总利息
            bb.setTotalRewardAmount(CalculatetUtil.calTotalInterest(bb.getReturnType(), bb.getBidRequestAmount(), bb.getCurrentRate(), bb.getMonthes2Return()));
            this.save(bb);
            //给申请人添加正在借款的状态码
            userInfo.addState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
            userInfoService.updateByPrimaryKey(userInfo);
        } else {
            throw new DisplayableExcetion("非法操作");
        }
    }

    //后台审核通过/退回,发表前审核
    @Override
    public void publishAudit(Long id, Date publishTime, Integer state, String remark) {
        //根据id查询街狂对象,借款对象处于借款申请待审核状态
        BidRequest bidRequest = this.get(id);
        if (bidRequest != null && bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_APPLY) {
            //记录谁审核的,审核时间,审核状态
            creaBidRequestAuditHistory(bidRequest, state, remark, BidRequestAuditHistory.TYPE_PUBLISH);
            if (state == BidRequestAuditHistory.STATE_PASS) {
                if (publishTime == null) {
                    bidRequest.setPublishTime(new Date());//马上发标
                    bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_BIDDING);//招标中
                } else {
                    bidRequest.setPublishTime(publishTime);//传入的发标时间
                    bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);//待发标
                }
                //设置招标截止时间
                bidRequest.setDisableDate(DateUtils.addDays(bidRequest.getPublishTime(), bidRequest.getDisableDays()));
            } else {
                //如果审核失败
                //设置借款的状态发标审核拒绝状态
                bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_REFUSE);
                //将申请人的借款状态码移除
                UserInfo createUserInfo = userInfoService.get(bidRequest.getCreateUser().getId());
                createUserInfo.removeState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
                userInfoService.updateByPrimaryKey(createUserInfo);
            }
            //风控意见
            bidRequest.setNote(remark);
            this.update(bidRequest);
        } else {
            throw new DisplayableExcetion("非法参数");
        }
    }

    @Override
    public List<BidRequest> queryPublishPendngBidRrequests() {
        BidRequestQueryObject qo = new BidRequestQueryObject();
        qo.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);
        return bidRequestMapper.selectAll(qo);
    }

    @Override
    public List<BidRequest> queryPendingBidRequest() {
        BidRequestQueryObject qo = new BidRequestQueryObject();
        qo.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);
        Date now = new Date();
        qo.setEndTime(DateUtils.addHours(now, 1));
        return bidRequestMapper.selectAll(qo);
    }

    @Override//查询首页数据
    public List<BidRequest> queryIndexData() {
        BidRequestQueryObject qo = new BidRequestQueryObject();
        //招标中,还款中,已还请
        qo.setStates(new int[]{Constants.BIDREQUEST_STATE_BIDDING, Constants.BIDREQUEST_STATE_PAYING_BACK, Constants.BIDREQUEST_STATE_COMPLETE_PAY_BACK});
        qo.setOrderByCondition("order by bidRequestState ");
        qo.setPageNum(1);
        qo.setPageSize(5);
        return bidRequestMapper.selectAll(qo);
    }

    //投标
    @Override
    public void bid(Long bidRequestId, BigDecimal amount) {
        //根据id查询借款对象,处于招标中的状态
        BidRequest bidRequest = this.get(bidRequestId);
        if (bidRequest != null && bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_BIDDING) {
            Account account = accountService.getCurrent();
            if (!UserContext.getCount().getId().equals(bidRequest.getCreateUser().getId()) &&//当前登录用户不等于借款人
                    amount.compareTo(bidRequest.getMinBidAmount()) >= 0 &&//投标的金额 ?= 设置最想投标金额
                    amount.compareTo(bidRequest.getRemainAmount().min(account.getUsableAmount())) <= 0 &&//投标金额<=min(借款还需金额,账户可用金额)
                    amount.compareTo(bidRequest.getRemainAmount().min(account.getUsableAmount())) <= 0
                    ) {
                Bid bid = new Bid();//投标对象
                bid.setActualRate(bidRequest.getCurrentRate());//街狂的年化利率
                bid.setAvailableAmount(amount);//投标金额
                bid.setBidRequestId(bidRequest.getId());//关联借款对象
                bid.setBidRequestState(bidRequest.getBidRequestState());//借款的状态,处于招标中
                bid.setBidRequestTitle(bidRequest.getTitle());//借款标题
                bid.setBidTime(new Date());//投标时间
                bid.setBidUser(UserContext.getCount());//投标人
                bidService.save(bid);
                //借款对象的属性设置
                //修改bidCount,currentSum属性
                bidRequest.setBidCount(bidRequest.getBidCount() + 1);//投标次数+1
                bidRequest.setCurrentSum(bidRequest.getCurrentSum().add(amount));
                //投资人的变化
                //投资人可用金额减少,冻结的金额增加
                account.setUsableAmount(account.getUsableAmount().subtract(amount));
                account.setFreezedAmount(account.getFreezedAmount().add(amount));
                accountService.updateByPrimaryKey(account);
                //添加投标冻结的流水
                accountFlowService.createCidFreecedGlow(account, amount);
                //判断金额是否投满
                if (bidRequest.getCurrentSum().compareTo(bidRequest.getBidRequestAmount()) >= 0) {
                    //价款金额 == 借款金额
                    //如果投满,就将借款的状态码改为满标一审
                    bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_APPROVE_PENDING_1);
                    //将投标的状态码改为满标一审
                    //update bid set bidRequestState = "" where bidRequestIs = #{}
                    bidService.updateState(bidRequest.getId(), Constants.BIDREQUEST_STATE_APPROVE_PENDING_1);
                }
                this.update(bidRequest);
            } else {
                throw new DisplayableExcetion("投标失败");
            }
        } else {
            throw new DisplayableExcetion("非法操作");
        }
    }

    //满标一审
    @Override
    public void audit(Long id, Integer state, String remark) {
        BidRequest bidRequest = this.get(id);
        if (bidRequest != null && bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_APPROVE_PENDING_1) {
            creaBidRequestAuditHistory(bidRequest, state, remark, BidRequestAuditHistory.TYPE_AUDIT1);
            if (state == BidRequestAuditHistory.STATE_PASS) {
                //如果审核通过
                bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_APPROVE_PENDING_2);//去到满标二审
                //对于借款对象和投标对象
                bidService.updateState(bidRequest.getId(), Constants.BIDREQUEST_STATE_APPROVE_PENDING_2);//更改当前的标的的状态
            } else {
                //审核失败
                auditRejuct(bidRequest);
            }
            this.update(bidRequest);
        } else {
            throw new DisplayableExcetion("非法操作");
        }
    }

    //创建流水历史
    private void creaBidRequestAuditHistory(BidRequest bidRequest, Integer state, String remark, int auditType) {
        BidRequestAuditHistory history = new BidRequestAuditHistory();
        history.setAuditType(auditType);
        history.setBidRequestId(bidRequest.getId());
        history.setState(state);
        history.setRemark(remark);
        history.setApplyTime(bidRequest.getApplyTime());
        history.setAuditor(UserContext.getCount());
        history.setApplier(bidRequest.getCreateUser());
        history.setAuditTime(new Date());
        bidRequestAuditHistoryService.save(history);
    }

    //审核失败
    private void auditRejuct(BidRequest bidRequest) {
        //审核失败,设置状态钱要回退,记录流水信息,记录审核人审核时间
        bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_REJECTED);
        bidService.updateState(bidRequest.getId(),Constants.BIDREQUEST_STATE_REJECTED);
        //计算每个投标人的投标总额
        List<Bid> bids = bidRequest.getBids();
        Map<Long,Account> accountMap = new HashMap<Long, Account>();
        Long bidAccountId;
        Account bidAccounr;
        for (Bid bid : bids) {
            bidAccountId = bid.getBidUser().getId();
            bidAccounr = accountMap.get(bidAccountId);
            if (bidAccounr == null) {
                bidAccounr = accountService.get(bidAccountId);
                accountMap.put(bidAccountId,bidAccounr);
            }
            //冻结金额减少
            bidAccounr.setFreezedAmount(bidAccounr.getFreezedAmount().subtract(bid.getAvailableAmount()));
            //可用金额增加
            bidAccounr.setUsableAmount(bidAccounr.getUsableAmount().add(bid.getAvailableAmount()));
        }
        //修改账户对象信息
        for (Account account : accountMap.values()) {
            accountService.updateByPrimaryKey(account);
        }
        //对于借款人的变化,授信额度增加,
        UserInfo bidRequestUserInfo = userInfoService.get(bidRequest.getCreateUser().getId());
        bidRequestUserInfo.removeState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
        userInfoService.updateByPrimaryKey(bidRequestUserInfo);
    }

    //满标二审
    @Override
    public void audit2(Long id, Integer state, String remark) {
        BidRequest bidRequest = this.get(id);
        if (bidRequest != null &&
                bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_APPROVE_PENDING_2) {
            creaBidRequestAuditHistory(bidRequest,state,remark,BidRequestAuditHistory.TYPE_AUDIT1);
            if (state == BidRequestAuditHistory.STATE_PASS) {
                bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_PAYING_BACK);
                bidService.updateState(bidRequest.getId(),Constants.BIDREQUEST_STATE_PAYING_BACK);
                Account bidrequestAccount = accountService.get(bidRequest.getCreateUser().getId());
                //可用金额
                bidrequestAccount.setUsableAmount(bidrequestAccount.getUsableAmount().add(bidRequest.getBidRequestAmount()));
                //待还金额
                bidrequestAccount.setUnReturnAmount(bidrequestAccount.getUnReturnAmount().add(bidRequest.getBidRequestAmount()).add(bidRequest.getTotalRewardAmount()));
                //剩余授信额度
                bidrequestAccount.setRemainBorrowLimit(bidrequestAccount.getRemainBorrowLimit().subtract(bidRequest.getBidRequestAmount()));
                //生成借款成功的流水
                accountFlowService.createBorrowSuressFlow(bidrequestAccount,bidRequest.getBidRequestAmount());
                //支付平台手续费
                BigDecimal accountManagementCharge = CalculatetUtil.calInterestManagerCharge(bidRequest.getBidRequestAmount());
                bidrequestAccount.setUsableAmount(bidrequestAccount.getUsableAmount().subtract(accountManagementCharge));
                accountService.updateByPrimaryKey(bidrequestAccount);
                accountFlowService.createAccountManagemrntChargeFlow(bidrequestAccount,accountManagementCharge);
                UserInfo userInfo = userInfoService.get(bidRequest.getCreateUser().getId());
                userInfo.removeState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
                userInfoService.updateByPrimaryKey(userInfo);
                //投标人账户
                List<Bid> bids = bidRequest.getBids();
                Account bidAccount;
                Long bidAccountId;
                Map<Long,Account> accountMap = new HashMap<Long, Account>();
                for (Bid bid : bids) {
                    bidAccountId = bid.getBidUser().getId();
                    bidAccount = accountMap.get(bidAccountId);
                    if (bidAccount == null){
                        bidAccount = accountService.get(bidRequest.getCreateUser().getId());
                        accountMap.put(bidAccountId,bidAccount);
                    }
                    //账户对象的变化
                    //冻结金额减少
                    bidAccount.setFreezedAmount(bidAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
                    //投标流水
                    accountFlowService.createVBidSuccessFlow(bidAccount,bid.getAvailableAmount());
                    //成成还款对象和还款明细
                    List<PaymentSchedule> paymentSchedules = createPaymentSchedules(bidRequest);
                    Account bidUserAccountTem;
                    for (PaymentSchedule ps : paymentSchedules) {
                        for (PaymentScheduleDetail psd : ps.getDetails()) {
                            bidUserAccountTem = accountMap.get(psd.getInvestorId());
                            bidUserAccountTem.setUnReceiveInterest(bidUserAccountTem.getUnReceiveInterest().add(psd.getInterest()));
                            bidUserAccountTem.setUnReceivePrincipal(bidUserAccountTem.getUnReceivePrincipal().add(psd.getPrincipal()));
                        }
                    }
                    //同一队投资用户进行更新操作
                    for (Account account : accountMap.values()) {
                        accountService.updateByPrimaryKey(account);
                    }
                }
            }else {
                //审核失败
                auditRejuct(bidRequest);
            }
        }else {
            throw new DisplayableExcetion("非法操作");
        }
    }

    //生成还款对象
    private List<PaymentSchedule> createPaymentSchedules(BidRequest bidRequest) {
        List<PaymentSchedule> paymentSchedules = new ArrayList<>();
        //创建几个还宽对象是由借款周期决定的
        BigDecimal totalAmountTem = Constants.ZERO;
        BigDecimal interestTemp = Constants.ZERO;
        PaymentSchedule ps;
        //创建还款对象,根据还款周期来创建对象
        for (int i =0;i<bidRequest.getMonthes2Return();i++) {
            ps = new PaymentSchedule();
            ps.setBidRequestId(bidRequest.getId());
            ps.setBidRequestTitle(bidRequest.getTitle());
            ps.setBorrowUser(bidRequest.getCreateUser());//借款人
            ps.setBidRequestType(bidRequest.getBidRequestType());
            ps.setReturnType(bidRequest.getReturnType());
            ps.setMonthIndex(i+1);
            //截止时间,发布时间加上一个月
            ps.setDeadLine(DateUtils.addMonths(bidRequest.getPublishTime(),i+1));
            //判断是否有最优一期还款
            if (i<bidRequest.getMonthes2Return()+1){
                //吐过不是最后一期,计算本期还款金额,还款类型,借款金额,年华利率,期数,借款期限
                ps.setTotalAmount(CalculatetUtil.calMonthToReturnMoney(bidRequest.getReturnType(),bidRequest.getBidRequestAmount(),
                        bidRequest.getCurrentRate(),i+1,bidRequest.getMonthes2Return()));
                //本息还款利息
                ps.setInterest(CalculatetUtil.calMonthlyInterest(bidRequest.getReturnType(),bidRequest.getBidRequestAmount(),
                        bidRequest.getCurrentRate(),i+1,bidRequest.getMonthes2Return()));
                //本期还款本金
                ps.setPrincipal(ps.getTotalAmount().subtract(ps.getInterest()));
                totalAmountTem = interestTemp.add(ps.getInterest());
                interestTemp = interestTemp.add(ps.getInterest());
            } else {
                //最后一期还款
                //最后一期还款金额=借款本金+借款利息 - n-1期还款金额之和
                ps.setTotalAmount(bidRequest.getTotalRewardAmount().add(bidRequest.getTotalRewardAmount().subtract(totalAmountTem)));
                //最后一期还款利息=借款利息-n+1期利息之和
                ps.setInterest(bidRequest.getTotalRewardAmount().subtract(interestTemp));
                //最后一期本金=最后一期还的金额+最后一期利息
                ps.setPrincipal(ps.getTotalAmount().subtract(ps.getInterest()));
            }
            paymentScheduleService.save(ps);
            //创建改期还款的明细集合对象
            createPaymentScheduleDetails(bidRequest, ps);
            paymentSchedules.add(ps);
        }
        return paymentSchedules;
    }

    //创建还款明细对象
    private void createPaymentScheduleDetails(BidRequest bidRequest, PaymentSchedule ps) {
        PaymentScheduleDetail psd;
        //创建几个还宽对象是由借款周期决定的
        BigDecimal principalTemp = Constants.ZERO;
        BigDecimal interestTemp = Constants.ZERO;
        //需要创建几个还款明细对象有投标的个数决定
        Bid bid;
        for (int i = 0;i<bidRequest.getBids().size();i++){
            bid = bidRequest.getBids().get(i);//获取投标对象
            psd = new PaymentScheduleDetail();
            psd.setBidAmount(bid.getAvailableAmount());//总投标金额=投标金额
            psd.setBidId(bid.getId());
            psd.setBidRequestId(bidRequest.getCreateUser().getId());
            psd.setBorrowUser(bidRequest.getCreateUser());
            psd.setReturnType(bidRequest.getReturnType());
            psd.setDeadLine(ps.getDeadLine());
            psd.setMonthIndex(ps.getMonthIndex());
            psd.setInvestorId(bid.getBidUser().getId());
            psd.setPaymentScheduleId(ps.getId());//所属还款计划
            //判断是不是最后一次投标
            if (i<bidRequest.getBids().size()-1){
                //不是最后一次投标,计算每个投标对象的投标金额占比
                BigDecimal rate = bid.getAvailableAmount().divide(bidRequest.
                        getBidRequestAmount(), Constants.SCALE_CAL, RoundingMode.HALF_UP);
                //本期还款本金
                psd.setPrincipal(ps.getPrincipal().multiply(rate).setScale(Constants.SCALE_STORE,RoundingMode.HALF_UP));
                //本期还款利息
                psd.setInterest(ps.getInterest().multiply(rate).setScale(Constants.SCALE_CAL,RoundingMode.HALF_UP));
                //本期还款总金额
                psd.setTotalAmount(psd.getPrincipal().add(psd.getInterest()));
                principalTemp = principalTemp.add(psd.getPrincipal());
                interestTemp = interestTemp.add(psd.getInterest());
            }else {
                //最后一次投标
                //该还款明细本金-(n+1还款明细本金之和)
                psd.setPrincipal(ps.getPrincipal().subtract(principalTemp));
                psd.setInterest(ps.getInterest().subtract(interestTemp));
            }
            paymentScheduleDetailService.save(psd);
            ps.getDetails().add(psd);
        }
    }
}
